package com.wawj.compplugin

import com.wawj.compplugin.utils.CmdUtil
import com.wawj.compplugin.utils.GitUtil
import groovy.swing.SwingBuilder
import org.gradle.BuildAdapter
import org.gradle.api.Plugin
import org.gradle.api.initialization.Settings
import org.gradle.api.invocation.Gradle
import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.NodeList

import javax.swing.JLabel
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory

/**
 * 按照《指向文件》和《配置切换文件》配置主工程对子仓库下的各组件的依赖关系，
 * 组件使用源码还是二进制，构建时组件是否包含。
 */
class MainRepoSettingsPlugin implements Plugin<Settings> {

    /**
     * hasConfigFile
     * true: 有配置文件构建apk，按照components_configs.xml配置文件
     * false：无配置文件构建apk，全部使用源码。Jenkins构建为无配置文件。
     */
    boolean hasConfigFile = false

    Settings settings
    String compRepoParentDir //子仓库clone到的目录
    boolean isJenkins = true//是否Jenkins构建
    boolean isContinue = false//非Jenkins构建时，没有本地配置文件弹框选择是否继续构建
    File pointsFile //指向文件的地址
    File configFile //配置文件的地址

    String aarGroup


    @Override
    void apply(Settings settings) {
        this.settings = settings

        settings.gradle.addBuildListener(new BuildAdapter() {
            @Override
            void settingsEvaluated(Settings s) {
                super.settingsEvaluated(s)
                println("------------->>主工程开始配置")
                init()
                assembleType()
                configProjectBuild()
                println("<<-------------主工程配置完成\n")

            }

            @Override
            void projectsEvaluated(Gradle gradle) {
                super.projectsEvaluated(gradle)
                //添加新建分支的Task
                gradle.rootProject.tasks.create("newBranch", NewBranchTask)
            }
        })

    }

    def init() {
        compRepoParentDir = settings.rootDir.absoluteFile.getParent()//clone的子仓库与主仓库平级目录
        isJenkins = System.getenv().JOB_NAME != null && !System.getenv().JOB_NAME.isEmpty()
        pointsFile = new File(settings.rootDir.absolutePath + "/components_points.xml")
        configFile = new File(settings.rootDir.absolutePath + "/components_configs.xml")
    }

    //确定构建方式
    def assembleType() {
        // 先判断是否Jenkins构建。
        if (isJenkins) {
            // Jenkins构建，防止components_configs.xml被误提交到远程仓库，先删除。
            if (configFile.exists()) {
                configFile.delete()
            }
            hasConfigFile = false
        } else {
            if (!configFile.exists()) {
                // 非jenkins下，没有配置文件弹框提示。
                // 选择“继续”，将按照无配置方式构建。
                // 选择“取消”，终止任务。
                showNoConfigFileDialog()
                if (isContinue) {
                    hasConfigFile = false
                } else {
                    throw new RuntimeException("无components_configs.xml，终止任务");
                }
            } else {
                hasConfigFile = true
            }
        }

        if (hasConfigFile) {
            println("有配置文件")
        } else {
            println("没有配置文件")
        }
    }


    def configProjectBuild() {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance()
        DocumentBuilder builder = factory.newDocumentBuilder()
        FileInputStream pointsInputStream = new FileInputStream(pointsFile)
        Document pointsDoc = builder.parse(pointsInputStream)
        Element pointsRootElement = pointsDoc.getDocumentElement()
        NodeList repoPointNodeList = pointsRootElement.getElementsByTagName("repo")
        Element mavenElement = pointsRootElement.getElementsByTagName("maven").item(0)
        aarGroup = mavenElement.getAttribute("aarGroup")
        //遍历子仓库
        for (int i = 0; i < repoPointNodeList.getLength(); i++) {
            Element repoPointElement = (Element) repoPointNodeList.item(i)
            String git = repoPointElement.getAttribute("git")
            String branch = repoPointElement.getAttribute("branch")
            String repoDirName = GitUtil.cutRepoName(git, true)
            File repoDirFile = new File(compRepoParentDir, repoDirName)
            //应用子仓库下的各组件的配置
            NodeList compPointNodeList = repoPointElement.getElementsByTagName("component")
            if (!hasConfigFile) {
                //无配置构建，子仓库下的所有组件都包含，使用源码。
                checkoutUseSrcSubRepo(git, repoDirFile, repoDirName, branch, compPointNodeList.findAll { true })
            } else {
                //有配置构建，子仓库下的组件应用components_configs.xml的配置
                applySubRepoConfig(compPointNodeList, git, repoDirFile, repoDirName, branch, configFile)
            }
        }
    }

    def checkoutUseSrcSubRepo(git, repoDirFile, repoDirName, branch, foundUseSrcElements) {
        if (foundUseSrcElements != null && foundUseSrcElements.size() > 0) {
            //被包含且有使用源码的，进行clone
            if (GitUtil.isGitDir(repoDirFile) && GitUtil.getOriginRemoteFetchUrl(repoDirFile) == git) {
                //此仓库本地已经存在，并且远程仓库地址相同
                if (isJenkins) {
                    println("${repoDirName}仓库已存在")
                    if (GitUtil.isLocalExistBranch(repoDirFile, branch)) {
                        println("${repoDirName} ${branch}分支本地已存在,Jenkins构建强制更新代码")
                        GitUtil.checkoutBranch(repoDirFile, branch)
                        GitUtil.forcePull(repoDirFile, branch)//强制覆盖更新，与远端代码一致。
                    } else {
                        println("${repoDirName} ${branch}分支本地不存在，checkout远程分支")
                        GitUtil.checkoutRemoteBranch(repoDirFile, branch)
                    }
                } else {
                    //开发者本地构建时，不更新代码、不切换到指定的分支，因为此时开发者不一定想切换分支、更新代码。
                    println("${repoDirName}仓库已存在，不再执行任何操作，如需切换分支、更新代码，请手动操作。")
                }

            } else {
                //此仓库本地不存在，或者与远程仓库地址不相同，
                //clone代码，并切到指定分支
                repoDirFile.delete()
                println("${repoDirName}仓库不存在，开始clone [" + git + "  " + branch + "]")
                GitUtil.clone(repoDirFile, git, branch)
                println("${repoDirName}仓库clone完成")
            }

            //include到主工程
            foundUseSrcElements.each {
                String compName = it.getAttribute("name")
                println("** ${compName} 使用源码")
            }

            settings.includeBuild(repoDirFile) {
                it.dependencySubstitution { dependency ->
                    //使用源码的，把aar替换成project依赖
                    foundUseSrcElements.each {
                        String compName = it.getAttribute("name")
                        dependency.substitute(dependency.module("${aarGroup}:$compName")).using(dependency.project(":$compName"))
                    }
                }
            }
        }
    }


    def applySubRepoConfig(compPointNodeList, git, repoDirFile, repoDirName, branch, configFile) {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance()
        DocumentBuilder builder = factory.newDocumentBuilder()
        FileInputStream configInputStream = new FileInputStream(configFile)
        Document configDoc = builder.parse(configInputStream)
        Element configRootElement = configDoc.getDocumentElement()
        NodeList compConfigNodeList = configRootElement.getElementsByTagName("component")
        CmdUtil.setDebug(configRootElement.getElementsByTagName("plugin_debug").item(0).getTextContent() == "true")

        //确定此repo中的组件是否有被include的
        List<Element> foundIncludeElements = compPointNodeList.findAll { compPoint ->
            String toBeFindName = ((Element) compPoint).getAttribute("name")
            Element foundConfigElement = compConfigNodeList.find { (((Element) it).getAttribute("name") == toBeFindName) }
            if (foundConfigElement == null) {
                throw new RuntimeException("components_configs.xml中没有${toBeFindName}组件的配置，请添加组件的配置。");
            } else {
                foundConfigElement.getAttribute("include") == "true"
            }
        }
        if (foundIncludeElements != null && foundIncludeElements.size() > 0) {
            //处理使用源码的组件
            List<Element> foundUseSrcElements
            if (configRootElement.getElementsByTagName("all_sourcecode").item(0).getTextContent() == "true") {
                foundUseSrcElements = foundIncludeElements
            }
            foundUseSrcElements = foundIncludeElements.findAll { compPoint ->
                Element foundConfigElement = compConfigNodeList.find { (((Element) it).getAttribute("name") == ((Element) compPoint).getAttribute("name")) }
                foundConfigElement.getAttribute("aar") != "true"
            }
            checkoutUseSrcSubRepo(git, repoDirFile, repoDirName, branch, foundUseSrcElements)

            //处理使用aar的组件
            List<Element> aarCompElements = foundIncludeElements.findAll { foundUseSrcElements == null || !foundUseSrcElements.contains(it) }
            aarCompElements.each {
                String compName = it.getAttribute("name")
                println("** ${compName} 使用aar")
            }

            settings.gradle.allprojects {
                configurations.all {
                    resolutionStrategy {
                        dependencySubstitution { dependency ->
                            aarCompElements.each {
                                String compName = it.getAttribute("name")
                                String aarVersion = it.getAttribute("aarVersion")
                                dependency.substitute(dependency.module("${aarGroup}:$compName")).using(dependency.module("${aarGroup}:$compName:$aarVersion"))
                            }
                        }
                    }
                }
            }
        }

        //没有被包含的组件不include，并且从各个模块(坑:不含includeBuild的模块)中去除依赖组件aar
        List<Element> excludeCompElements = compPointNodeList.findAll { foundIncludeElements == null || !foundIncludeElements.contains(it) }
        excludeCompElements.each {
            String compName = it.getAttribute("name")
            println("** ${compName} 不包含")
        }
        settings.gradle.allprojects {
            configurations {
                excludeCompElements.each {
                    String compName = it.getAttribute("name")
                    implementation { exclude group: aarGroup, module: compName }
                    implementation { exclude group: rootProject.name, module: compName }
                }
            }
        }
    }


    def showNoConfigFileDialog() {
        if (System.console() == null) {
            System.setProperty('java.awt.headless', 'false')
            new SwingBuilder().edt {
                dialog(modal: true, size: [400, 170], title: '提示', alwaysOnTop: true, resizable: true, locationRelativeTo: null, pack: false, show: true) {
                    vbox() {
                        label(text: " ")
                        label(text: "没有components_configs.xml切换配置文件", horizontalAlignment: JLabel.LEFT)
                        label(text: " ")
                        label(text: "点击“继续“将使用源码且全量构建")
                        label(text: "点击“取消“终止任务")
                        label(text: " ")
                        hbox() {
                            button(defaultButton: true, text: '继续', actionPerformed: {
                                dispose()
                                isContinue = true
                            })
                            label(text: "")
                            button(defaultButton: false, text: '取消', actionPerformed: {
                                dispose()
                                isContinue = false
                            })
                        }
                    }
                }
            }
        } else {
            isContinue = false
        }
    }
}


//class CompSettingsExtension {
//    boolean pluginDebug = false
//}